//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.common.mybaits;

import com.common.base.Pager;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Intercepts({@Signature(
    type = StatementHandler.class,
    method = "prepare",
    args = {Connection.class, Integer.class}
)})
public class PageInterceptor implements Interceptor {
    private static final Logger log = Logger.getLogger(PageInterceptor.class.getName());
    private static Pattern pattern = Pattern.compile("(\\w+\\.)?deleted *= *((false)|0)", 2);

    public PageInterceptor() {
    }

    private String insertTimestampCondition(String sql, Long timestamp) {
        Matcher matcher = pattern.matcher(sql);
        StringBuilder result = null;
        int previousEndIndex = 0;

        while(matcher.find()) {
            if (result == null) {
                result = new StringBuilder(sql.substring(0, matcher.end()));
            } else {
                result.append(sql.substring(previousEndIndex, matcher.end()));
            }

            previousEndIndex = matcher.end();
            if (timestamp != null) {
                result.append(" and ");
                result.append(matcher.group(1) != null ? matcher.group(1) : "");
                result.append("ts < ");
                result.append(timestamp);
            }
        }

        result.append(sql.substring(previousEndIndex));
        return result.toString();
    }

    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement)metaStatementHandler.getValue("delegate.mappedStatement");
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        if (!"SELECT".equals(sqlCommandType.name())) {
            return invocation.proceed();
        } else {
            Object object;
            while(metaStatementHandler.hasGetter("h")) {
                object = metaStatementHandler.getValue("h");
                metaStatementHandler = SystemMetaObject.forObject(object);
            }

            while(metaStatementHandler.hasGetter("target")) {
                object = metaStatementHandler.getValue("target");
                metaStatementHandler = SystemMetaObject.forObject(object);
            }

            BoundSql boundSql = (BoundSql)metaStatementHandler.getValue("delegate.boundSql");
            Pager pager = null;
            Object param = boundSql.getParameterObject();
            if (param instanceof Pager) {
                pager = (Pager)param;
            } else if (param instanceof Map) {
                Iterator var9 = ((Map)param).entrySet().iterator();

                while(var9.hasNext()) {
                    Entry<String, Object> e = (Entry)var9.next();
                    if (e.getValue() instanceof Pager) {
                        pager = (Pager)e.getValue();
                        break;
                    }
                }
            }

            if (pager != null) {
                PagerContextHolder.setPager(pager);
                Pager originPager = Pager.pager(pager.getPage(), pager.getPageSize(), pager.getTimestamp());
                String originalSql = boundSql.getSql().replaceAll("\\s+", " ");
                if (originPager.getTimestamp() != null && !Long.valueOf(0L).equals(originPager.getTimestamp())) {
                    originalSql = this.insertTimestampCondition(originalSql, originPager.getTimestamp());
                }

//                log.warning("original sql: " + originalSql);
                metaStatementHandler.setValue("delegate.boundSql.sql", this.buildPageSql(originalSql, originPager));
                CountTimestamp countTimestamp = this.queryTotal(mappedStatement, boundSql.getParameterMappings(), boundSql.getParameterObject(), originalSql, originPager);
                pager.setTotalRecord(countTimestamp.getTotalRecord());
                if (countTimestamp.getTimestamp() != null) {
                    pager.setTimestamp(countTimestamp.getTimestamp().getTime());
                }
            }

            return invocation.proceed();
        }
    }

    private CountTimestamp queryTotal(MappedStatement mappedStatement, List<ParameterMapping> parameterMappings, Object parameterObject, String originalSql, Pager originPager) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        String countSql = this.buildCountSql(originalSql, originPager.getTimestamp());
//        log.warning("count sql: " + countSql);
        CountTimestamp countTimestamp = new CountTimestamp();

        try {
            connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
            statement = connection.prepareStatement(countSql);
            BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, parameterObject);
            (new DefaultParameterHandler(mappedStatement, countBoundSql.getParameterObject(), countBoundSql)).setParameters(statement);
            resultSet = statement.executeQuery();
            if (resultSet.next()) {
                countTimestamp.setTotalRecord(resultSet.getLong(1));
                if (Long.valueOf(0L).equals(originPager.getTimestamp())) {
                    countTimestamp.setTimestamp(resultSet.getTimestamp(2));
                } else if (originPager.getTimestamp() != null) {
                    countTimestamp.setTimestamp(new Date(originPager.getTimestamp()));
                }
            }
        } catch (SQLException var20) {
            var20.printStackTrace();
            log.warning("Execption when execute sql " + countSql + ", because of " + var20.getMessage());
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }

                if (statement != null) {
                    statement.close();
                }

                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException var19) {
                var19.printStackTrace();
                log.warning("Execption when close resetSet or statement, because of " + var19.getMessage());
            }

        }

        return countTimestamp;
    }

    private String buildCountSql(String originalSql, Long timestamp) {
        return "select count(0)" + (Long.valueOf(0L).equals(timestamp) ? ", current_timestamp(3) " : "") + " from (" + originalSql + ") __pager__ ";
    }

    public Object plugin(Object target) {
        return !(target instanceof StatementHandler) && !(target instanceof ResultSetHandler) ? target : Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {
    }

    private String buildPageSql(String sql, Pager pager) {
        int start = pager.getPage() > 0 ? (pager.getPage() - 1) * pager.getPageSize() : 0;
        return sql + " limit " + start + ", " + pager.getPageSize();
    }

    private class CountTimestamp {
        private long totalRecord;
        private Date timestamp;

        private CountTimestamp() {
        }

        public long getTotalRecord() {
            return this.totalRecord;
        }

        public void setTotalRecord(long totalRecord) {
            this.totalRecord = totalRecord;
        }

        public Date getTimestamp() {
            return this.timestamp;
        }

        public void setTimestamp(Date timestamp) {
            this.timestamp = timestamp;
        }
    }
}
